package wml.clipstore.storage.clips;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import wml.clipstore.base.Entity;
import wml.clipstore.map.MapManager;
import wml.clipstore.utils.EntityUtils;
import CLIPSJNI.Environment;

class ClipsSaver {

	private MapManager manager;
	private Environment clips;

	ClipsSaver(MapManager manager, Environment clips) {
		this.manager = manager;
		this.clips = clips;
	}

	public void save(Entity entity) {
		List<String> facts = this.toFacts(entity);
		this.deffacts(facts);
	}

	public void save(Collection<? extends Entity> entities) {
		List<String> facts = this.toFacts(entities);
		this.deffacts(facts);
	}

	private void deffacts(List<String> facts) {
		// StringBuilder deffacts = new StringBuilder("(deffacts facts")
		// .append(new Date().getTime());
		// for (String fact : facts)
		// deffacts.append(fact);
		// deffacts.append(")");
		// System.out.println(deffacts);
		for (String fact : facts){
			//System.out.println(fact);
			clips.assertString(fact);
		}
	}

	private List<String> toFacts(Collection<? extends Entity> entities) {
		List<String> facts = new ArrayList<String>();
		Set<Integer> es = new HashSet<Integer>();
		es.add(entities.hashCode());
		for (Object o : entities) {
			Entity entity = (Entity) o;
			this.deepIn(entity, es, facts);
		}
		return facts;
	}

	private List<String> toFacts(Entity entity) {
		Set<Integer> entities = new HashSet<Integer>();
		List<String> facts = new ArrayList<String>();
		this.deepIn(entity, entities, facts);
		return facts;
	}

	private void deepIn(Entity entity, Set<Integer> es, List<String> facts) {
		if (es.contains(entity.hashCode()))
			return;
		es.add(entity.hashCode());
		StringBuilder fact = new StringBuilder("(").append(entity.getClass()
				.getName());
		List<Field> fields = manager.getStoredFields(entity.getClass());
		for (Field f : fields) {
			Object value = EntityUtils.getValue(entity, f);
			if (EntityUtils.isSubClass(f.getType(), Entity.class)) {
				fact.append("(").append(f.getName()).append(" ").append(value)
						.append(")");
				this.deepIn((Entity) value, es, facts);
			} else if (EntityUtils.isSubClass(f.getType(), Collection.class)) {
				fact.append("(").append(f.getName()).append(" ")
						.append(value.hashCode()).append("@")
						.append(value.getClass().getName()).append(")");
				this.deepIn((Collection<?>) value, es, facts);
			} else {
				String className = (value == null) ? f.getType().getName()
						: value.getClass().getName();
				fact.append("(").append(f.getName()).append(" \"")
						.append(value).append("@").append(className)
						.append("\"").append(")");
			}
		}
		fact.append(")");
		facts.add(fact.toString());
	}

	private void deepIn(Collection<?> entities, Set<Integer> es,
			List<String> facts) {
		if (es.contains(entities.hashCode()))
			return;
		es.add(entities.hashCode());
		StringBuilder fact = new StringBuilder("(Collection")
				.append(" (identifier ").append(entities.hashCode())
				.append("@").append(entities.getClass().getName())
				.append(") (items");
		for (Object value : entities) {
			if (EntityUtils.isSubClass(value.getClass(), Entity.class)) {
				fact.append(" ").append(value);
				this.deepIn((Entity) value, es, facts);
			} else if (EntityUtils.isSubClass(value.getClass(),
					Collection.class)) {
				fact.append(" ").append(value.hashCode()).append("@")
						.append(entities.getClass().getName());
				this.deepIn((Collection<?>) value, es, facts);
			} else {
				fact.append(" ").append(value);
			}
		}
		facts.add(fact.append("))").toString());
	}
}
